#include "BusinessLogicController.h"

#include <set>
#include <map>
#include <sstream>
#include <boost/foreach.hpp>
#include "MembershipHistoryManager.h"
#include "SessionManager.h"
#include "SessionPresenceManager.h"
#include "CouncilMemberManager.h"
#include "ChairmanshipHistoryManager.h"
#include "ObjectModel.h"

std::vector<CouncilMember> BusinessLogicController::currentCommitteeMembers( const Committee& committee )
{
  std::vector<CouncilMember> result;

  const MembershipHistoryManager::MembershipHistoryMap &histories = 
    m_model.membershipHistory().histories();

  BOOST_FOREACH (const MembershipHistoryManager::MembershipHistoryPair &p, histories)
  {
    if (p.second.committeeId() == committee.id() && p.second.exitDate() == c_nullDate)
      result.push_back(*m_model.councilMembers().memberById(p.second.memberId()));
  }
  return result;
}

CouncilMember BusinessLogicController::getCurrentCommetteeChairmanship( const Committee& committee )
{
  CouncilMember result;

  const ChairmanshipHistoryManager::ChairmanshipHistoryMap &histories = 
    m_model.chairmanshipHistory().histories();

  BOOST_FOREACH (const ChairmanshipHistoryManager::ChairmanshipHistoryPair &p, histories)
  {
    if (p.second.committeeId() == committee.id() && p.second.exitDate() == c_nullDate)
      result = *m_model.councilMembers().memberById(p.second.memberId());	  
  }
  return result;
}

std::vector<CouncilMember> BusinessLogicController::getLatest10YearsChairmen( const Committee& committee )
{
  std::vector<CouncilMember> result;

  const ChairmanshipHistoryManager::ChairmanshipHistoryMap &histories = 
    m_model.chairmanshipHistory().histories();

  time_t tenYears = makeTime(1980,1,1);

  BOOST_FOREACH (const ChairmanshipHistoryManager::ChairmanshipHistoryPair &p, histories)
  {
    if (p.second.committeeId() == committee.id() && p.second.exitDate() != c_nullDate 
      && (time(0) - p.second.exitDate())<=tenYears)
      result.push_back(*m_model.councilMembers().memberById(p.second.memberId()));
  }
  return result;
}

std::vector<time_t> BusinessLogicController::chairmenEntryDatesByCommittee( const Committee& committee )
{
  std::vector<time_t> result;

  const ChairmanshipHistoryManager::ChairmanshipHistoryMap &histories = 
    m_model.chairmanshipHistory().histories();    	

  BOOST_FOREACH (const ChairmanshipHistoryManager::ChairmanshipHistoryPair &p, histories)
  {
    if (p.second.committeeId() == committee.id())
      result.push_back(p.second.entryDate());
  }
  return result;
}

std::vector<Committee> BusinessLogicController::committeesByMember( const CouncilMember& member )
{
  //note that by business logic chairmanship also means membersip.
  //So, we should only check membership history

  std::vector<Committee> result;

  const MembershipHistoryManager::MembershipHistoryMap &histories = 
    m_model.membershipHistory().histories();

  BOOST_FOREACH (const MembershipHistoryManager::MembershipHistoryPair &p, histories)
  {
    if (p.second.memberId() == member.id())
      result.push_back(*m_model.committees().committeeById(p.second.committeeId()));
  }
  return result;
}

int BusinessLogicController::getSessionCount( const Committee& committee, const time_t startDate, const time_t endDate )
{
  int result = 0;
  const SessionManager::SessionMap &sessions = m_model.sessions().sessions();

  BOOST_FOREACH (const SessionManager::SessionPair &p, sessions)
  {
    if (p.second.committeeId() == committee.id() && p.second.date() >= startDate && p.second.date() <= endDate) 
      ++result;
  }
  return result;
}

std::string BusinessLogicController::addCommittee(const std::string& committeeAim, const std::string& chairmanId)
{
  const std::string newCommitteeId = m_model.committees().newCommitteeId();
  m_model.committees().insert(Committee(newCommitteeId, committeeAim));

  const std::string newHistoryId = m_model.chairmanshipHistory().newHistoryId();
  m_model.chairmanshipHistory().insert(ChairmanshipHistory(newHistoryId, chairmanId, newCommitteeId, time(0), c_nullDate));

  const std::string newMembershipHistoryId = m_model.membershipHistory().newHistoryId();
  m_model.membershipHistory().insert(MembershipHistory(newMembershipHistoryId, chairmanId, newCommitteeId, time(0), c_nullDate));

  return newCommitteeId;
}

std::vector<CouncilMember> BusinessLogicController::committeeMembersWithinDateRange(const Committee& committee,
                                                                   time_t startDate, time_t endDate)
{
  std::set<CouncilMember> resultSet;
  BOOST_FOREACH(const MembershipHistoryManager::MembershipHistoryPair &p, m_model.membershipHistory().histories())
  {
    const bool rangesOverlap = p.second.entryDate() <= endDate &&
      (p.second.exitDate() >= startDate || p.second.exitDate() == c_nullDate);
    if (p.second.committeeId() == committee.id() && rangesOverlap)
      resultSet.insert(*m_model.councilMembers().memberById(p.second.memberId()));
  }
  
  std::vector<CouncilMember> result;
  std::copy(resultSet.begin(), resultSet.end(), std::back_inserter(result));
  return result;
}

std::vector<Session> BusinessLogicController::committeeSessionsWithinDateRange(const Committee& committee,
                                                                               time_t startDate, time_t endDate)
{
  std::vector<Session> result;
  BOOST_FOREACH(const SessionManager::SessionPair &p, m_model.sessions().sessions())
  {
    const bool withinTheRange = startDate <= p.second.date() && p.second.date() <= endDate;
    if (p.second.committeeId() == committee.id() && withinTheRange)
      result.push_back(p.second);
  }
  return result;
}

bool BusinessLogicController::memberWasAbsent(const Session& session, const CouncilMember& member)
{
  BOOST_FOREACH(const SessionPresenceManager::SessionPresencePair &p, m_model.sessionPresence().presenceRecords())
  {
    if (p.second.sessionId() == session.id() && p.second.memberId() == member.id())
      return false;
  }
  return true;
}

std::string BusinessLogicController::listOfAbsence(const Committee& committee, const time_t startDate, const time_t endDate)
{
  typedef std::map<CouncilMember, int> AbsenceMap;
  typedef std::pair<CouncilMember, int> AbsencePair;

  std::vector<CouncilMember> membersForPeriod = committeeMembersWithinDateRange(committee, startDate, endDate);
  std::vector<Session> sessionsForPeriod = committeeSessionsWithinDateRange(committee, startDate, endDate);
  
  AbsenceMap absences;
  BOOST_FOREACH(const CouncilMember& member, membersForPeriod)
  {
    int count = 0;
    BOOST_FOREACH(const Session& session, sessionsForPeriod)
    {
      if (memberWasAbsent(session, member))
        ++count;
    }
    absences.insert(std::make_pair(member, count));
  }

  std::ostringstream stream;
  BOOST_FOREACH(const AbsencePair &p, absences)
    stream << p.first.name() << " was absent " << p.second << " times" << std::endl;
  
  return stream.str();
}

std::string BusinessLogicController::addSession(time_t date, const Committee& committee,
                                                std::vector<CouncilMember> members)
{
  const std::string newSessionId = m_model.sessions().newSessionId();
  m_model.sessions().insert(Session(newSessionId, committee.id(), date));
  BOOST_FOREACH(const CouncilMember &member, members)
  {
    std::string newPresenceRecordId = m_model.sessionPresence().newPresenceRecordId();
    m_model.sessionPresence().insert(SessionPresence(newPresenceRecordId, newSessionId, member.id()));
  }
  return newSessionId;
}
